using Godot;
using System;
using System.Collections.Generic;

public partial class TileMap : Godot.TileMap {
    public static readonly Random random = new();
    public bool GameOver = false;
    public Vector2I RangeLU = new(0, 0);
    public Vector2I RangeRD = new(4, 4);
    public enum Direction {
        Up,
        Down,
        Left,
        Right
    };
    public override void _Ready() {
        AddNewCell();
        AddNewCell();
    }
    public override void _Input(InputEvent @event) {
        if (GameOver) {
            return;
        }
        if (@event is InputEventAction action) {
            if (action.IsPressed()) {
                return;
            }
            if (action.Action == "up") {
                Move(Direction.Up);
            }
            if (action.Action == "down") {
                Move(Direction.Down);
            }
            if (action.Action == "left") {
                Move(Direction.Left);
            }
            if (action.Action == "right") {
                Move(Direction.Right);
            }
        }
    }
    public int GetCell(Vector2I position) {
        TileData tileData = GetCellTileData(0, position);
        if (tileData == null) {
            return -1;
        }
        try {
            int ret = (int) tileData.GetCustomData("num");
            return ret;
        } catch (Exception) {
            return -1;
        }
    }
    public void SetCell(Vector2I position, int tile) {
        SetCell(0, position, 0, new Vector2I(tile % 4, tile / 4));
    }
    public Vector2I RandomPosition() {
        return new Vector2I(random.Next(RangeLU.X, RangeRD.X), random.Next(RangeLU.Y, RangeRD.Y));
    }
    public void Move(Direction direction) {
        int upTo1024 = 0;
        if (direction == Direction.Up || direction == Direction.Down) {
            for (int x = RangeLU.X; x < RangeRD.X; x++) {
                List<Vector2I> cells = new();
                for (int y = RangeLU.Y; y < RangeRD.Y; y++) {
                    cells.Add(new Vector2I(x, y));
                }
                if (direction == Direction.Down) {
                    cells.Reverse();
                }
                upTo1024 += DealWithCells(cells);
            }
        }
        if (direction == Direction.Left || direction == Direction.Right) {
            for (int y = RangeLU.Y; y < RangeRD.Y; y++) {
                List<Vector2I> cells = new();
                for (int x = RangeLU.X; x < RangeRD.X; x++) {
                    cells.Add(new Vector2I(x, y));
                }
                if (direction == Direction.Right) {
                    cells.Reverse();
                }
                upTo1024 += DealWithCells(cells);
            }
        }
        // 生成新方块
        if (!AddNewCell()) {
            GD.Print("游戏结束");
            GameOver = true;
        }
        // 超过1024扩张地图
        for (int i = 0; i < upTo1024; i++) {
            ExpandMap();
        }
    }
    /// <summary>
    /// 加块
    /// </summary>
    /// <param name="check">是否检查游戏结束</param>
    /// <returns>游戏是否结束</returns>
    public bool AddNewCell(bool check = true) {
        if (check) {
            if (CheckGameOver()) {
                return false;
            }
        }
        Vector2I position = RandomPosition();
        if (GetCell(position) == 0) {
            SetCell(position, random.Next()%2+1);
            return true;
        }
        AddNewCell(false);
        return true;
    }
    public void ExpandMap() {
        Vector2I position = new(0, 0);
        while (GetCell(position) != -1) {
            position += new Vector2I(random.Next()%3-1, random.Next()%3-1);
        }
        SetCell(position, 0);
        if (position.X < RangeLU.X) {
            RangeLU.X--;
        }
        if (position.Y < RangeLU.Y) {
            RangeLU.Y--;
        }
        if (position.X >= RangeRD.X) {
            RangeRD.X++;
        }
        if (position.Y >= RangeRD.Y) {
            RangeRD.Y++;
        }
    }
    public bool CheckGameOver() {
        for (int x = RangeLU.X; x < RangeRD.X; x++) {
            for (int y = RangeLU.Y; y < RangeRD.Y; y++) {
                if (GetCell(new Vector2I(x, y)) == 0) {
                    return false;
                }
            }
        }
        return true;
    }
    public int DealWithCells(List<Vector2I> cells) {
        int upTo1024 = 0;

        // 步骤1：根据墙壁分割成独立子组
        var subgroups = new List<List<Vector2I>>();
        var currentGroup = new List<Vector2I>();
        
        foreach (var pos in cells) {
            int tile = GetCell(pos);
            if (tile == -1) { // 遇到墙壁
                if (currentGroup.Count > 0) {
                    subgroups.Add(currentGroup);
                    currentGroup = new List<Vector2I>();
                }
            } else {
                currentGroup.Add(pos);
            }
        }
        if (currentGroup.Count > 0) subgroups.Add(currentGroup);

        // 步骤2：处理每个独立子组
        foreach (var group in subgroups) {
            // 收集当前组的有效值（排除墙壁）
            var tiles = new List<int>();
            foreach (var pos in group) {
                tiles.Add(GetCell(pos));
            }

            // 步骤3：移动非零元素到前端
            var compact = new List<int>();
            foreach (var t in tiles) {
                if (t > 0) compact.Add(t);
            }

            // 步骤4：合并相邻相同元素
            var merged = new List<int>();
            for (int i = 0; i < compact.Count; i++) {
                if (i < compact.Count - 1 && compact[i] == compact[i + 1]) {
                    merged.Add(compact[i] + 1); // 合并后tile值+1
                    i++; // 跳过下一个已合并元素
                } else {
                    merged.Add(compact[i]);
                }
            }

            // 步骤5：将1024及以上的值转换为0
            for (int i = 0; i < merged.Count; i++) {
                if (merged[i] >= 10) {
                    merged[i] = 0;
                    upTo1024++;
                }
            }

            // 步骤6：填充结果并更新单元格
            for (int i = 0; i < group.Count; i++) {
                int newValue = i < merged.Count ? merged[i] : 0;
                SetCell(group[i], newValue);
            }
        }
        return upTo1024;
    }
}
